package net.ssta.core.db;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import net.ssta.core.util.UUIDGenerator;
import net.ssta.core.util.format.ValueObjectFormat;


public abstract class DataBaseExecutor
{
	protected Connection m_conn = null;
	public static DataBaseExecutor getExecutor(Connection conn)
	{
		DataBaseExecutor executor = null;
		int dbtype = DataBaseType.getConnectionDBType(conn);
		switch(dbtype)
		{
			case DataBaseType.DB_MYSQL  : executor = new MysqlDataBaseExecutor(conn);break;
			case DataBaseType.DB_ORACLE  : executor =  new OracleDataBaseExecutor(conn);break;
			case DataBaseType.DB_MSSQL  : executor =  new SQLServerDataBaseExecutor(conn);break;
			case DataBaseType.DB_UNKNOWN : break;
			default : throw new RuntimeException("unsupported db type!");
		}
		return executor;
	}
	
	protected DataBaseExecutor(Connection conn){m_conn = conn;}
	
	public Connection getConnection(){return m_conn;}
	
	/**
	 * ʵ�ֹ����Ҫ�Ľ�RecordSet�е������������ʵ�壨�?�У�����RecordSetʵ���е�Record������һ�µĽṹ
	 * @param recordSet
	 * @return
	 * @throws DBException 
	 */
	public int create(RecordSet recordSet) throws SQLException
	{

		for(int i=0; i<recordSet.size(); i++)
		{
			create(recordSet.get(i));
		}
		return recordSet.size();
//		PreparedStatement pstmt = null;
//		try
//		{
//			if(recordSet.size() <=0) return 0;
//			
//			Record record = recordSet.get(0);
//			//���SQL : INSERT INTO EntityName(col1,col2,col3,...) VALUES (?,?,?,...)
//			StringBuffer sb = new StringBuffer("INSERT INTO ");
//			sb.append(record.getEntityName()).append(" ( ");
//			StringBuffer placeholderBuffer = new StringBuffer();
//			String[] fields = record.getFields();
//			for (int i = 0; i < fields.length; i++)
//			{
//				if (i > 0) 
//				{
//					sb.append(",");
//					placeholderBuffer.append(",");
//				}
//				sb.append(fields[i]);
//				placeholderBuffer.append("?");
//			}
//			sb.append(" ) VALUES ( ").append(placeholderBuffer).append(" )");
//			
//			Map<String, Integer> entryColumnTypeCode = getEntryColumnTypeCode(recordSet.getEntityName());
//			pstmt = m_conn.prepareStatement(sb.toString());
//			
//			for(int i=0; i<recordSet.size(); i++)
//			{
//				record = recordSet.get(i);				
//				//���������Զ����
//				fullPrimaryKeyValueWithUuid( record );
//				setPreparedStatementParameter(pstmt,record.getFields(), 1, entryColumnTypeCode, record);
//				pstmt.addBatch();
//			}
//			return pstmt.executeUpdate();
//		}
//		catch (SQLException e)
//		{
//			throw new DBException(e);
//		}
//		finally 
//		{
//			try 
//			{
//				if (pstmt != null)
//				{
//					pstmt.close();
//				}
//			}
//			catch (SQLException e) 
//			{
//			}
//		}
	}
	
	/**
	 * ��ʵ�壨�?�����һ���¼�����Record���ڿ�ֵ���������Զ���UUID���
	 * @param record
	 * @return
	 * @throws DBException
	 */
	protected int create(Record record) throws SQLException
	{
		PreparedStatement pstmt = null;
		try
		{
			//���������Զ����
			fullPrimaryKeyValueWithUuid( record );
			//���SQL : INSERT INTO EntityName(col1,col2,col3,...) VALUES (?,?,?,...)
			StringBuffer sb = new StringBuffer("INSERT INTO ");
			sb.append(record.getEntityName()).append(" ( ");
			StringBuffer placeholderBuffer = new StringBuffer();
			String[] fields = record.getFields();
			for (int i = 0; i < fields.length; i++)
			{
				if (i > 0) 
				{
					sb.append(",");
					placeholderBuffer.append(",");
				}
				sb.append(fields[i]);
				placeholderBuffer.append("?");
			}
			sb.append(" ) VALUES ( ").append(placeholderBuffer).append(" )");
			pstmt = m_conn.prepareStatement(sb.toString());
			Map<String, Integer> entryColumnTypeCode = getEntryColumnTypeCode(record.getEntityName());
			setPreparedStatementParameter(pstmt,record.getFields(), 1, entryColumnTypeCode, record);
			return pstmt.execute() ? 1 : 0;
		}
		catch (SQLException e)
		{
			throw e;
		}
		finally 
		{
			try 
			{
				if (pstmt != null)
				{
					pstmt.close();
				}
			}
			catch (SQLException e) 
			{
			}
		}
	}

	
	/**
	 * ��Recordʵ���е���ݸ��µ�ʵ�壨�?�ж�Ӧ�ļ�¼�У����Record���ڿ�ֵ���������׳��쳣
	 * @param record
	 * @return
	 * @throws DBException
	 */
	protected int update(Record record) throws SQLException
	{
		PreparedStatement pstmt = null;
		try
		{
			//���SQL : UPDATE EntityName SET col1=?,col2=?,col3=?,... WHERE pk1=?,pk2=?,...

			String[] fields = record.getFields();
			String[] primaryKeyFields = record.getPrimaryKeyFields();
			String[] fieldsExceptPK = new String[fields.length - primaryKeyFields.length];
			
			//���û����Ҫ���µ��ֶΣ���ֱ�ӷ���0
			if(fieldsExceptPK.length == 0) return 0;
			
			StringBuffer sb = new StringBuffer("UPDATE ");
			sb.append(record.getEntityName()).append(" SET ");
			for (int i = 0,j = 0; i < fields.length; i++)
			{
				String field = fields[i];
				if(!record.containsPrimaryKey(field))
				{
					if (j > 0)
					{
						sb.append(",");
					}
					sb.append(fields[i]);
					sb.append("=?");
					fieldsExceptPK[j++] = field;
				}
			}
			
			//����WHERE CLAUSE		
			sb.append(" WHERE 1=1 ");			
			for (int i = 0; i < primaryKeyFields.length; i++)
			{
				String pk = primaryKeyFields[i];
				Object pkValue = record.getObjectValue(pk);
				if (pkValue == null || "".equals(pkValue) || "null".equals(pkValue))
				{
					throw new DBException("some of the key column is not evaluated, the column name is " + pk);
				}
				else
				{
					sb.append("AND ");
					sb.append(pk);
					sb.append("=? ");
				}
			}
			
			pstmt = m_conn.prepareStatement(sb.toString());
			Map<String, Integer> entryColumnTypeCode = getEntryColumnTypeCode(record.getEntityName());
			setPreparedStatementParameter(pstmt,fieldsExceptPK, 1, entryColumnTypeCode, record);
			setPreparedStatementParameter(pstmt,primaryKeyFields, fieldsExceptPK.length + 1, entryColumnTypeCode, record);
			return pstmt.executeUpdate();
		}
		finally 
		{
			try 
			{
				if (pstmt != null)
				{
					pstmt.close();
				}
			}
			catch (SQLException e) 
			{
			}
		}
	}
	
	/**
	 * ʵ�ֹ����Ҫ�Ľ�RecordSet�е���������µ�ʵ�壨�?�У�����RecordSetʵ���е�Record������һ�µĽṹ
	 * @param recordSet
	 * @return
	 */
	public int update(RecordSet recordSet) throws SQLException
	{
		for(int i=0; i<recordSet.size(); i++)
		{
			update(recordSet.get(i));
		}
		return recordSet.size();
	}
	
	/**
	 * ʵ�ֹ����Ҫ�Ľ�
	 * @param recordSet
	 * @return
	 * @throws SQLException 
	 */
	public int delete(RecordSet recordSet) throws SQLException
	{
		for(int i=0; i<recordSet.size(); i++)
		{
			delete(recordSet.get(i));
		}
		return recordSet.size();
	}
	
	/**
	 * ��record�е������Ϊ�����ɾ��������ļ�¼
	 * @param record
	 * @return
	 * @throws DBException
	 */
	protected int delete(Record record) throws SQLException
	{
		PreparedStatement pstmt = null;
		try
		{
			//���SQL : DELETE FROM EntityName WHERE pk1=?,pk2=?,...

			String[] primaryKeyFields = record.getPrimaryKeyFields();
			StringBuffer sb = new StringBuffer("DELETE FROM ");
			sb.append(record.getEntityName());
			
			//����WHERE CLAUSE		
			sb.append(" WHERE ");
			if(primaryKeyFields.length == 0)
			{
				throw new DBException("you must specify one column as the primary key at least "); 
			}
			for (int i = 0; i < primaryKeyFields.length; i++)
			{
				String pk = primaryKeyFields[i];
				Object pkValue = record.getObjectValue(pk);
				if (pkValue == null || "".equals(pkValue) || "null".equals(pkValue))
				{
					throw new DBException("some of the key column is not evaluated, the column name is " + pk);
				}
				else
				{
					if (i > 0)
					{
						sb.append(" AND ");
					}
					sb.append(pk);
					sb.append("=?");
				}
			}
			
			pstmt = m_conn.prepareStatement(sb.toString());
			Map<String, Integer> entryColumnTypeCode = getEntryColumnTypeCode(record.getEntityName());
			setPreparedStatementParameter(pstmt,primaryKeyFields, 1, entryColumnTypeCode, record);
			return pstmt.executeUpdate();
		}
		finally 
		{
			try 
			{
				if (pstmt != null)
				{
					pstmt.close();
				}
			}
			catch (SQLException e) 
			{
			}
		}
	}
	
	/**
	 * ��ʱûʵ��
	 * @param entryName
	 * @param fieldName
	 * @param values
	 * @return
	 */
	public int delete(String entryName, String fieldName, List values)
	{
		return 0;
	}
	
	/**
	 * ִ��ָ����SQL��䣬�������Ӱ��ļ�¼������
	 * @param sql
	 * @return
	 * @throws DBException 
	 */
	public int execute(String sql) throws SQLException
	{
		PreparedStatement pstmt = null;
		try 
		{
			pstmt = m_conn.prepareStatement(sql);
			return  pstmt.executeUpdate();
		}
		finally 
		{
			try 
			{
				if (pstmt != null)
				{
					pstmt.close();
				}
			}
			catch (SQLException e) 
			{
			}
		}
	}
	
	/**
	 * ��ݴ����SQL�����в��ң����ص�һ��������ļ�¼�����û���ҵ����򷵻�null
	 * @param sql
	 * @return
	 * @throws DBException
	 */
	public Record findFirst(String sql) throws SQLException
	{
		RecordSet recs = find(sql);
		return (recs != null && recs.size() > 0) ? recs.get(0) : null;
	}
	
	/**
	 * ��ݴ����SQL�����в��ң����ط������ļ�¼��
	 * @param sql
	 * @return
	 * @throws DBException
	 */
	public RecordSet find(String sql) throws SQLException
	{
		RecordSet records = new RecordSet();
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		try 
		{
			pstmt = m_conn.prepareStatement(sql);
			rs = pstmt.executeQuery();
			ResultSetMetaData rsmd = rs.getMetaData();
			Map<String, String> entryColumnType = getEntryColumnType(rsmd);
			
			while (rs.next())
			{
				Record record = fillRecord(rs, rsmd, entryColumnType);
				records.add(record);
			}
			records.setRawSQL(sql);
			return records;
		}
		finally 
		{
			try 
			{
				if (rs != null) 
				{
					rs.close();
				}
				if (pstmt != null)
				{
					pstmt.close();
				}
			}
			catch (SQLException e) 
			{
			}
		}
	}
	
	/**
	 * ��ݴ����SQL�����з�ҳ���ң����ط������ļ�¼��
	 * @param sql ��ѯ���
	 * @param pagingInfo  ��ҳ��Ϣ����(ÿ��ҳ��ļ�¼��Ͳ�ѯ�ڼ�ҳ
	 * @return
	 * @throws SQLException 
	 */
	public RecordSet find(String sql, PagingInfo pagingInfo) throws SQLException
	{
//		���Ƕ��
//		SELECT * FROM
//		(
//			SELECT TBL.*,ROWNUM RN FROM 
//			(
//				SELECT * FROM T WHERE ... ORDER BY...
//			)TBL
//			WHERE  ROWNUM<=30
//		)
//		WHERE RN>=20;
		
//		����row_number��������з�ҳ(Ч�ʸ��)
//
//		SELECT xx.* FROM(
//		SELECT t.*,row_number() over(ORDER BY o_id)AS num
//		FROM t_order t
//		)xx
//		WHERE num BETWEEN 5 AND 15;
				
//		List<String> selectColumnList = getFieldsFromSqlStatment(sql);
//		StringBuffer selectedFields = new StringBuffer("");
//		for(int i = 0; i<selectColumnList.size(); i++ )
//		{
//			selectedFields.append(selectColumnList.get(i));
//			selectedFields.append(",");
//		}
//		selectedFields.setCharAt(selectedFields.length() - 1, ' ');
		
		int iPageSize = pagingInfo.getPageSize();
		int iPageNo = pagingInfo.getCurrentPageNo();
		
		int iRecordCount = getRecordTotalCount(sql);
		pagingInfo.setTotalRecordCount(iRecordCount);
		
		StringBuffer sb = new StringBuffer();
		sb.append("SELECT *  FROM (");
		//sb.append("SELECT TBL.*,ROWNUM RN FROM (");
		sb.append(sql);
		sb.append(")TBL  limit ").append((iPageNo-1) * iPageSize);
		sb.append(", ").append(iPageSize);
		RecordSet records = find(sb.toString());
		records.setRawSQL(sql);
		return records;
	}
	
	private int getRecordTotalCount(String sql) throws SQLException
	{
		StringBuffer sb = new StringBuffer();
		sb.append("SELECT COUNT(*) AS CC FROM (");
		sb.append(sql);
		sb.append(") A");
		
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		try 
		{
			pstmt = m_conn.prepareStatement(sb.toString());
			rs = pstmt.executeQuery();			
			return rs.next() ? rs.getInt("CC") :0;
		}
		finally 
		{
			try 
			{
				if (rs != null) 
				{
					rs.close();
				}
				if (pstmt != null)
				{
					pstmt.close();
				}
			}
			catch (SQLException e) 
			{
			}
		}
	}
	
	protected RecordSet find(Record record, String[] columns, String[] order, PagingInfo pagingInfo) throws SQLException
	{
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		RecordSet records = new RecordSet();
		try
		{	
			String[] conditionFields = record.getFields();
			String sql = buildSql4FindByRecord(record, columns, conditionFields, order);
			pstmt = m_conn.prepareStatement(sql);
			
			Map<String, Integer> entryColumnTypeCode = getEntryColumnTypeCode(record.getEntityName());
			setPreparedStatementParameter(pstmt,conditionFields, 1, entryColumnTypeCode, record);
			
			rs = pstmt.executeQuery();
			ResultSetMetaData rsmd = rs.getMetaData();
			Map<String, String> entryColumnType = getEntryColumnType(rsmd);
			while (rs.next())
			{
				Record newRecord = fillRecord(rs, rsmd, entryColumnType);
				records.add(newRecord);
			}
			return records;
		}
		finally 
		{
			try 
			{
				if (rs != null) 
				{
					rs.close();
				}
				if (pstmt != null)
				{
					pstmt.close();
				}
			}
			catch (SQLException e) 
			{
			}
		}
	}
	
	/**
	 * ��Recordʵ��ָ���ı������е����
	 * @param record ��Ϊ�����Record
	 * @param selectFields ָ����ѯ��Ҫ����Щ��
	 * @param order ָ����ѯ����������
	 * @return
	 * @throws SQLException 
	 * @throws DBException
	 */
	protected RecordSet findAll(Record record, String[] columns,String[] order) throws SQLException
	{
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		RecordSet records = new RecordSet();
		try
		{	
			String sql = buildSql4FindByRecord(record, columns, null, order);
			pstmt = m_conn.prepareStatement(sql);			
			rs = pstmt.executeQuery();
			ResultSetMetaData rsmd = rs.getMetaData();
			Map<String, String> entryColumnType = getEntryColumnType(rsmd);
			while (rs.next())
			{
				Record newRecord = fillRecord(rs, rsmd, entryColumnType);
				records.add(newRecord);
			}
			return records;
		}
		finally 
		{
			try 
			{
				if (rs != null) 
				{
					rs.close();
				}
				if (pstmt != null)
				{
					pstmt.close();
				}
			}
			catch (SQLException e) 
			{
			}
		}
	}
	
	/**
	 * ��Recordʵ���������е�ֵΪ��������ø÷���ʱ�����е�����ж�����Ϊ��ֵ
	 * @param record ��Ϊ�����Record
	 * @param selectFields ָ����ѯ��Ҫ����Щ��
	 * @return
	 * @throws DBException
	 */
	protected Record findByPrimaryKey(Record record,String[] selectFields) throws SQLException
	{
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		
		try
		{	
			String[] primaryKeyFields = record.getPrimaryKeyFields();
			String sql = buildSql4FindByRecord(record, selectFields, primaryKeyFields, null);
			pstmt = m_conn.prepareStatement(sql);
			Map<String, Integer> entryColumnTypeCode = getEntryColumnTypeCode(record.getEntityName());
			setPreparedStatementParameter(pstmt,primaryKeyFields, 1, entryColumnTypeCode, record);
			rs = pstmt.executeQuery();
			ResultSetMetaData rsmd = rs.getMetaData();
			Map<String, String> entryColumnType = getEntryColumnType(rsmd);
			Record ret = null;
			if (rs.next())
			{
				ret = fillRecord( rs, rsmd, entryColumnType);
			}
			return ret;
		}
		finally 
		{
			try 
			{
				if (rs != null) 
				{
					rs.close();
				}
				if (pstmt != null)
				{
					pstmt.close();
				}
			}
			catch (SQLException e) 
			{
			}
		}
	}
	
	protected void fullPrimaryKeyValueWithUuid(Record record)
	{
		//���������Զ����
		Iterator<String> pkIterator = record.primaryKeyIterator();
		while (pkIterator.hasNext())
		{
			String pk = pkIterator.next();
			Object pkValue = record.getObjectValue(pk);
			if (pkValue == null || "".equals(pkValue) || "null".equals(pkValue))
			{
				record.addData(pk, UUIDGenerator.getUUID());
			}
		}
	}
	
	protected void setPreparedStatementParameter(PreparedStatement pstmt, String[] fields, int baseParamIndex, Map<String, Integer> entryColumnTypeCode,Record record) throws SQLException
	{
		for (int i = 0; i < fields.length; i++)
		{
			String columnName = fields[i];
			Object columnValue = record.getObjectValue(columnName);
			ValueObjectFormat formatter = ValueObjectFormat.getFormat(columnValue);
			Integer columnTypeCode = entryColumnTypeCode.get(columnName);
			if(columnTypeCode == null) throw new RuntimeException("the column "+columnName+" is undefined,please check it and try again.");
			setPreparedStatementParameter(pstmt, i+baseParamIndex, columnTypeCode, columnValue, formatter);
		}
	}
	
	protected Map<String, String> getEntryColumnType(String entryName) throws SQLException
	{
		PreparedStatement ps = null;
		ResultSet rs = null;
		try
		{
			StringBuffer sb = new StringBuffer("SELECT * FROM ").append(entryName).append(" WHERE 1=2");
			ps = m_conn.prepareStatement(sb.toString());
			rs = ps.executeQuery();
			return getEntryColumnType(rs.getMetaData());
		}
		finally 
		{
			try
			{
				if (rs != null)
				{
					rs.close();
				}
				if (ps != null)
				{
					ps.close();
				}
			}
			catch(Exception e)
			{
			}
		}
	}
	
	protected Map<String, String> getEntryColumnType(ResultSetMetaData rsmd) throws SQLException
	{
		Map<String, String> retMap = new HashMap<String, String>();
		for (int i = 1; i <= rsmd.getColumnCount(); i++)
		{
			retMap.put(rsmd.getColumnName(i).toUpperCase(), rsmd.getColumnClassName(i));
		}
		return retMap;
	}
	
	protected Map<String, Integer> getEntryColumnTypeCode(String entryName) throws SQLException
	{
		PreparedStatement ps = null;
		ResultSet rs = null;
		try
		{
			StringBuffer sb = new StringBuffer("SELECT * FROM ").append(entryName).append(" WHERE 1=2");
			ps = m_conn.prepareStatement(sb.toString());
			rs = ps.executeQuery();
			
			Map<String, Integer> retMap = new HashMap<String, Integer>();
			ResultSetMetaData rsmd = rs.getMetaData();;
			for (int i = 1; i <= rsmd.getColumnCount(); i++)
			{
				retMap.put(rsmd.getColumnName(i).toUpperCase(), rsmd.getColumnType(i));
			}
			return retMap;
		}
		finally 
		{
			try
			{
				if (rs != null)
				{
					rs.close();
				}
				if (ps != null)
				{
					ps.close();
				}
			}
			catch(Exception e)
			{
			}
		}
	}
	
	abstract protected void setPreparedStatementParameter(PreparedStatement pstmt, int paramIndex, int columnTypeCode, Object paramValue, ValueObjectFormat formatter) throws SQLException;

	abstract protected Record fillRecord(ResultSet rs, ResultSetMetaData rsmd, Map<String, String> entryColumnType) throws SQLException;
	
	private String buildSql4FindByRecord(Record record, String[] selectFields, String[] conditionFields, String[] order)
	{
		//���SQL : SELECT col1,col2,col3,... FROM EntityName WHERE pk1=?,pk2=?,...
		StringBuffer sb = new StringBuffer("SELECT ");
		if(selectFields == null || selectFields.length == 0)
		{
			sb.append(" * ");
		}
		else
		{
			for (int i = 0; i < selectFields.length; i++)
			{
				sb.append(selectFields[i]);
				sb.append(",");
			}
			sb.setCharAt(sb.length()-1, ' ');
		}
		sb.append(" FROM ");
		sb.append(record.getEntityName());
		//����WHERE CLAUSE		
		
		if(conditionFields != null)
		{
			sb.append(" WHERE 1=1 ");
			if(conditionFields.length == 0)
			{
				throw new DBException("you do not set the search condition, at least one condition column was specified!");
			}
			for (int i = 0; i < conditionFields.length; i++)
			{
				String column = conditionFields[i];
				Object pkValue = record.getObjectValue(column);
				if (pkValue == null || "".equals(pkValue) || "null".equals(pkValue))
				{
					throw new DBException("some of the condition column is not evaluated, the column name is " + column);
				}
				else
				{
					sb.append("AND ");
					sb.append(column);
					sb.append("=? ");
				}
			}
		}
		if(order != null)
		{
			sb.append(" ORDER BY ");
			for (int i = 0; i < order.length; i++)
			{
				sb.append(order[i]).append(",");
			}
			sb.setCharAt(sb.length() - 1, ' ');
		}
		return sb.toString();
	}
	
	/**
	 * ��ȡSELECT ����е����������Ƶ��У�ʹ��ǰ�������в��Խ�����ȷ��
	 * @param sql
	 * @return
	 */
	private List<String> getFieldsFromSqlStatment(String sql)
	{
		List<String> fields = new ArrayList<String>();
		//�������˵����������Ĵ��?���Ƕ�ǰһ����õ��Ľ����е�
		//�ҳ�SELECT ... FROM ֮��Ĳ���
		String s = sql;
		Matcher m = null;//select_pattern.matcher(s);
		//if(m.find())
		{
			//s = m.group(1);
			//ȥ��(�ż�����������ݣ���ѯ���õ����У����Ậ��(��
			Pattern parenthesis_pattern = Pattern.compile("\\([^\\(\\)]*\\)");
			while(parenthesis_pattern.matcher(s).find())
			{
				s = s.replaceAll("\\([^\\(\\)]*\\)", " ");
			}
			
			Pattern select_pattern = Pattern.compile("\\s*SELECT\\s+(.*?\\s+FROM)\\s+.*",Pattern.CASE_INSENSITIVE);
			m = select_pattern.matcher(s);
			if(m.find())
			{
				s = m.group(1);
			}

			//��ȡ����е�����
			Pattern fields_pattern = Pattern.compile("\\s*([^\\s\\.]*)\\s*(,|FROM)",Pattern.CASE_INSENSITIVE);
			m = fields_pattern.matcher(s);
			while(m.find())
			{
				fields.add(m.group(1));
			}
		}
		return fields;
	}
}
